/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "xpm_webview_test.h"

#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>

#include "file_ex.h"
#include "xpm_common.h"

using namespace std;
using namespace std::chrono;
using namespace testing;
using namespace testing::ext;

namespace OHOS {
namespace Security {
namespace XpmUnitTest {
static int g_fd;
static unsigned long g_dataOffset, g_dataSize;
static unsigned long g_codeOffset, g_codeSize;

void XpmWebviewTest::SetUpTestCase()
{
    SaveStringToFile(SELINUX_MODE_PATH, PERMISSIVE_MODE);

    g_fd = open(XPM_LIB_PATH.c_str(), O_RDWR);
    ASSERT_NE(g_fd, -1) << "open xpm lib failed: " << strerror(errno);

    int ret = GetDataSegment(XPM_LIB_PATH, g_dataOffset, g_dataSize);
    ASSERT_EQ(0, ret) << "GetDataSegment failed: " << ret;

    ret = GetCodeSegment(XPM_LIB_PATH, g_codeOffset, g_codeSize);
    ASSERT_EQ(0, ret) << "GetCodeSegment failed: " << ret;

    ret = SetCon(SELINUX_TYPE_NWEBSPAWN);
    ASSERT_EQ(0, ret) << "SetCon failed: " << strerror(errno);

    SaveStringToFile(XPM_DEBUG_FS_MODE_PATH, ENFORCE_MODE);
}

void XpmWebviewTest::TearDownTestCase()
{
    close(g_fd);
    SetCon(SELINUX_TYPE_SH);
    SaveStringToFile(XPM_DEBUG_FS_MODE_PATH, PERMISSIVE_MODE);
    SaveStringToFile(SELINUX_MODE_PATH, ENFORCE_MODE);
}

void XpmWebviewTest::SetUp()
{
}

void XpmWebviewTest::TearDown()
{
}

/**
 * @tc.number 2
 * @tc.name: FileMmapDataToRxTest
 * @tc.desc: Webview数据段文件映射到r-x权限内存
 * @tc.result: 映射失败，数据段不允许映射执行权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileMmapDataToRxTest, TestSize.Level0)
{
    // 1. webview进程映射数据段为r-x权限
    void *addr = mmap(NULL, g_dataSize, PROT_READ | PROT_EXEC, MAP_PRIVATE, g_fd, g_dataOffset & PAGE_MASK);
    EXPECT_EQ(MAP_FAILED, addr);
}

/**
 * @tc.number 3
 * @tc.name: FileMmapCodeToRxTest
 * @tc.desc: Webview代码段文件映射到r-x权限内存
 * @tc.result: 映射成功，代码段允许映射执行权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileMmapCodeToRxTest, TestSize.Level0)
{
    // 1. webview进程映射代码段为r-x权限
    void *addr = mmap(NULL, g_codeSize, PROT_READ | PROT_EXEC, MAP_PRIVATE, g_fd, g_codeOffset & PAGE_MASK);
    EXPECT_NE(MAP_FAILED, addr);

    munmap(addr, g_codeSize);
}

/**
 * @tc.number 4
 * @tc.name: FileMmapDataToRwxTest
 * @tc.desc: Webview数据段文件映射到rwx权限内存
 * @tc.result: 映射失败，文件映射不允许同时映射wx权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileMmapDataToRwxTest, TestSize.Level0)
{
    // 1. webview进程映射数据段为rwx权限
    void *addr = mmap(NULL, g_dataSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, g_fd, g_dataOffset & PAGE_MASK);
    EXPECT_EQ(MAP_FAILED, addr);
}

/**
 * @tc.number 5
 * @tc.name: FileMmapCodeToRwxTest
 * @tc.desc: 二Webview代码段文件映射到rwx权限内存
 * @tc.result: 映射失败，文件映射不允许同时映射wx权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileMmapCodeToRwxTest, TestSize.Level0)
{
    // 1. webview进程映射代码段为rwx权限
    void *addr = mmap(NULL, g_codeSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, g_fd, g_codeOffset & PAGE_MASK);
    EXPECT_EQ(MAP_FAILED, addr);
}

/**
 * @tc.number 6
 * @tc.name: FileMprotectDataRoToRxTest
 * @tc.desc: Webview数据段文件映射的r--权限内存通过mprotect修改为r-x权限
 * @tc.result: 修改操作失败，数据段不允许映射x权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileMprotectDataRoToRxTest, TestSize.Level0)
{
    // 1. webview进程映射数据段为r--权限
    void *addr = mmap(NULL, g_dataSize, PROT_READ, MAP_PRIVATE, g_fd, g_dataOffset & PAGE_MASK);
    EXPECT_NE(MAP_FAILED, addr);

    // 2. 修改映射为r-x权限
    int ret = mprotect(addr, g_dataSize, PROT_READ | PROT_EXEC);
    EXPECT_NE(0, ret);

    munmap(addr, g_dataSize);
}

/**
 * @tc.number 7
 * @tc.name: FileMprotectDataRwToRwxTest
 * @tc.desc: Webview数据段文件映射的rw-权限内存通过mprotect修改为rwx权限
 * @tc.result: 修改操作失败，数据段不允许映射x权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileMprotectDataRwToRwxTest, TestSize.Level0)
{
    // 1. webview进程映射数据段为rw-权限
    void *addr = mmap(NULL, g_dataSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, g_fd, g_dataOffset & PAGE_MASK);
    EXPECT_NE(MAP_FAILED, addr);

    // 2. 修改映射为rwx权限
    int ret = mprotect(addr, g_dataSize, PROT_READ | PROT_WRITE | PROT_EXEC);
    EXPECT_NE(0, ret);

    munmap(addr, g_dataSize);
}

/**
 * @tc.number 8
 * @tc.name: FileMprotectCodeRoToRxTest
 * @tc.desc: Webview代码段文件映射的r--权限内存通过mprotect修改为r-x权限
 * @tc.result: 修改操作成功，代码段允许映射rx权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileMprotectCodeRoToRxTest, TestSize.Level0)
{
    // 1. webview进程映射代码段为rw-权限
    void *addr = mmap(NULL, g_codeSize, PROT_READ, MAP_PRIVATE, g_fd, g_codeOffset & PAGE_MASK);
    EXPECT_NE(MAP_FAILED, addr);

    // 2. 修改映射为r-x权限
    int ret = mprotect(addr, g_codeSize, PROT_READ | PROT_EXEC);
    EXPECT_EQ(0, ret);

    munmap(addr, g_codeSize);
}

/**
 * @tc.number 9
 * @tc.name: FileMprotectCodeRxToRwxTest
 * @tc.desc: Webview代码段文件映射的r-x权限内存通过mprotect修改为rwx权限
 * @tc.result: 修改操作失败，代码段允许映射同时映射wx权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileMprotectCodeRxToRwxTest, TestSize.Level0)
{
    // 1. webview进程映射代码段为r-x权限
    void *addr = mmap(NULL, g_codeSize, PROT_READ | PROT_EXEC, MAP_PRIVATE, g_fd, g_codeOffset & PAGE_MASK);
    EXPECT_NE(MAP_FAILED, addr);

    // 2. 修改映射为rwx权限
    int ret = mprotect(addr, g_codeSize, PROT_READ | PROT_WRITE | PROT_EXEC);
    EXPECT_NE(0, ret);

    munmap(addr, g_codeSize);
}

/**
 * @tc.number 10
 * @tc.name: FileMprotectDataRwToRwxTest
 * @tc.desc: Webview代码段文件映射的rw-权限内存通过mprotect修改为rwx权限
 * @tc.result: 修改操作失败，代码段允许映射同时映射wx权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileMprotectCodeRwToRwxTest, TestSize.Level0)
{
    // 1. webview进程映射代码段为rw-权限
    void *addr = mmap(NULL, g_codeSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, g_fd, g_codeOffset & PAGE_MASK);
    EXPECT_NE(MAP_FAILED, addr);

    // 2. 修改映射为rwx权限
    int ret = mprotect(addr, g_codeSize, PROT_READ | PROT_WRITE | PROT_EXEC);
    EXPECT_NE(0, ret);

    munmap(addr, g_codeSize);
}

/**
 * @tc.number 12
 * @tc.name: FileMprotectDataRwToRwxTest
 * @tc.desc: Webview数据段文件映射的r--权限内存通过mremap修改为r-x权限
 * @tc.result: 修改操作失败，数据段不允许映射x权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileRemapDataRoToRxTest, TestSize.Level0)
{
    // 1. webview进程映射数据段为r--权限
    void *addr1 = mmap(NULL, g_dataSize, PROT_READ, MAP_PRIVATE, g_fd, g_dataOffset & PAGE_MASK);
    EXPECT_NE(MAP_FAILED, addr1);

    // 2. 重映射
    void *addr2 = mremap(addr1, g_dataSize, g_dataSize, MREMAP_MAYMOVE);
    EXPECT_NE(MAP_FAILED, addr2);

    // 3. 修改重映射为r-x权限
    int ret = mprotect(addr2, g_dataSize, PROT_READ | PROT_EXEC);
    EXPECT_NE(0, ret);

    munmap(addr2, g_dataSize);
}

/**
 * @tc.number 13
 * @tc.name: FileRemapDataRwToRxTest
 * @tc.desc: Webview数据段文件映射的rw-权限内存通过mremap修改为r-x权限
 * @tc.result: 修改操作失败，数据段不允许映射x权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileRemapDataRwToRxTest, TestSize.Level0)
{
    // 1. webview进程映射数据段为rw-权限
    void *addr1 = mmap(NULL, g_dataSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, g_fd, g_dataOffset & PAGE_MASK);
    EXPECT_NE(MAP_FAILED, addr1);

    // 2. 重映射
    void *addr2 = mremap(addr1, g_dataSize, g_dataSize, MREMAP_MAYMOVE);
    EXPECT_NE(MAP_FAILED, addr2);

    // 3. 修改重映射为r-x权限
    int ret = mprotect(addr2, g_dataSize, PROT_READ | PROT_EXEC);
    EXPECT_NE(0, ret);

    munmap(addr2, g_dataSize);
}

/**
 * @tc.number 14
 * @tc.name: FileRemapDataRwToRwxTest
 * @tc.desc: Webview数据段文件映射的rw-权限内存通过mremap修改为rwx权限
 * @tc.result: 修改操作失败，数据段不允许映射x权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileRemapDataRwToRwxTest, TestSize.Level0)
{
    // 1. webview进程映射数据段为rw-权限
    void *addr1 = mmap(NULL, g_dataSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, g_fd, g_dataOffset & PAGE_MASK);
    EXPECT_NE(MAP_FAILED, addr1);

    // 2. 重映射
    void *addr2 = mremap(addr1, g_dataSize, g_dataSize, MREMAP_MAYMOVE);
    EXPECT_NE(MAP_FAILED, addr2);

    // 3. 修改重映射为rwx权限
    int ret = mprotect(addr1, g_dataSize, PROT_READ | PROT_WRITE | PROT_EXEC);
    EXPECT_NE(0, ret);

    // release resource
    munmap(addr2, g_dataSize);
}

/**
 * @tc.number 15
 * @tc.name: FileRemapCodeRoToRxTest
 * @tc.desc: Webview代码段文件映射的r--权限内存通过mremap修改为r-x权限
 * @tc.result: 修改操作成功，代码段允许映射x权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileRemapCodeRoToRxTest, TestSize.Level0)
{
    // 1. webview进程映射代码段为r--权限
    void *addr1 = mmap(NULL, g_codeSize, PROT_READ, MAP_PRIVATE, g_fd, g_codeOffset & PAGE_MASK);
    EXPECT_NE(MAP_FAILED, addr1);

    // 2. 重映射
    void *addr2 = mremap(addr1, g_codeSize, g_codeSize, MREMAP_MAYMOVE);
    EXPECT_NE(MAP_FAILED, addr2);

    // 3. 修改重映射为r-x权限
    int ret = mprotect(addr2, g_codeSize, PROT_READ | PROT_EXEC);
    EXPECT_EQ(0, ret);

    munmap(addr2, g_codeSize);
}

/**
 * @tc.number 16
 * @tc.name: FileRemapCodeRwToRwxTest
 * @tc.desc: Webview代码段文件映射的rw-权限内存通过mremap修改为rwx权限
 * @tc.result: 修改操作失败，映射不允许同时拥有wx权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileRemapCodeRwToRwxTest, TestSize.Level0)
{
    // 1. webview进程映射代码段为rw-权限
    void *addr1 = mmap(NULL, g_codeSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, g_fd, g_codeOffset & PAGE_MASK);
    ASSERT_NE(MAP_FAILED, addr1);

    // 2. 重映射
    void *addr2 = mremap(addr1, g_codeSize, g_codeSize, MREMAP_MAYMOVE);
    ASSERT_NE(MAP_FAILED, addr2);

    // 3. 修改重映射为rwx权限
    int ret = mprotect(addr2, g_codeSize, PROT_READ | PROT_WRITE | PROT_EXEC);
    EXPECT_NE(0, ret);

    // release resource
    munmap(addr2, g_codeSize);
}

/**
 * @tc.number 17
 * @tc.name: FileMprotectCodeRwToRwxTest
 * @tc.desc: Webview代码段文件映射的rw-权限内存通过mremap修改为r-x权限
 * @tc.result: 修改操作成功，执行代码段时失败，代码段不允许先设置rw权限，后设置rx权限（同普通进程）
 */
HWTEST_F(XpmWebviewTest, FileRemapCodeRwToRxTest, TestSize.Level0)
{
    // 1. webview进程映射代码段为rw-权限
    void *addr1 = mmap(NULL, g_codeSize, PROT_READ | PROT_WRITE, MAP_PRIVATE, g_fd, g_codeOffset & PAGE_MASK);
    ASSERT_NE(MAP_FAILED, addr1);

    // 2. 重映射
    void *addr2 = mremap(addr1, g_codeSize, g_codeSize, MREMAP_MAYMOVE);
    ASSERT_NE(MAP_FAILED, addr2);

     // 3. 修改重映射为r-x权限
    int ret = mprotect(addr2, g_codeSize, PROT_READ | PROT_EXEC);
    EXPECT_EQ(0, ret);

    munmap(addr2, g_codeSize);
}

/**
 * @tc.number 18
 * @tc.name: AnonMmapRwTest
 * @tc.desc: Webview匿名映射rw-权限内存
 * @tc.result: 匿名映射成功，rw-匿名内存不管控
 */
HWTEST_F(XpmWebviewTest, AnonMmapRwTest, TestSize.Level0)
{
    // 1. webview进程匿名映射rw-权限
    void *addr = mmap(NULL, ANON_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    EXPECT_NE(MAP_FAILED, addr);

    munmap(addr, ANON_SIZE);
}

/**
 * @tc.number 19
 * @tc.name: AnonMmapRwxTest
 * @tc.desc: weebview匿名映射rwx权限内存
 * @tc.result: 匿名映射成功，webview匿名映射x权限内存（同debug_hap进程）
 */
HWTEST_F(XpmWebviewTest, AnonMmapRwxTest, TestSize.Level0)
{
    // 1. webview进程匿名映射rwx权限
    void *addr = mmap(NULL, ANON_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    EXPECT_NE(MAP_FAILED, addr);
}

/**
 * @tc.number 20
 * @tc.name: AnonMprotectRwToRwxTest
 * @tc.desc: webview匿名映射的rw权限内存通过mprotect修改为rwx匿名内存
 * @tc.result: 修改webview匿名内存为rwx成功，webview匿名映射x权限内存（同debug_hap进程）
 */
HWTEST_F(XpmWebviewTest, AnonMprotectRwToRwxTest, TestSize.Level0)
{
    // 1. 匿名映射映射rw-内存
    void *addr = mmap(NULL, ANON_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    EXPECT_NE(MAP_FAILED, addr);

    // 2. 修改匿名内存为rwx
    int ret = mprotect(addr, ANON_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC);
    EXPECT_EQ(0, ret);

    munmap(addr, ANON_SIZE);
}

/**
 * @tc.number 21
 * @tc.name: AnonMprotectRwToRwxTest
 * @tc.desc: webview匿名映射的rw-权限内存通过mprotect修改为r--匿名内存,之后再修改为r-x匿名内存
 * @tc.result: 修改匿名内存为rx成功，webview匿名映射x权限内存（同debug_hap进程）
 */
HWTEST_F(XpmWebviewTest, AnonMprotectRwToRoToRxTest, TestSize.Level0)
{
    // 1. 匿名映射映射rw-内存
    void *addr = mmap(NULL, ANON_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    EXPECT_NE(MAP_FAILED, addr);

    // 2. 修改匿名内存为r--
    int ret = mprotect(addr, ANON_SIZE, PROT_READ);
    EXPECT_EQ(0, ret);

    // 3. 修改匿名内存为r-x
    ret = mprotect(addr, ANON_SIZE, PROT_READ | PROT_EXEC);
    EXPECT_EQ(0, ret);

    munmap(addr, ANON_SIZE);
}

/**
 * @tc.number 22
 * @tc.name: AnonRmapRwToRxTest
 * @tc.desc: webview匿名映射的rw-权限内存重映射到r-x匿名内存
 * @tc.result: 重映射到r-x成功，webview匿名映射x权限内存（同debug_hap进程）
 */
HWTEST_F(XpmWebviewTest, AnonRmapRwToRxTest, TestSize.Level0)
{
    // 1. 匿名映射映射rw-内存
    void *addr1 = mmap(NULL, ANON_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    EXPECT_NE(MAP_FAILED, addr1);

    // 2. 重映射匿名内存
    void *addr2 = mremap(addr1, ANON_SIZE, NEW_ANON_SIZE, MREMAP_MAYMOVE);
    EXPECT_NE(MAP_FAILED, addr2);

    // 3. 修改匿名内存为r-x
    int ret = mprotect(addr2, NEW_ANON_SIZE, PROT_READ | PROT_EXEC);
    EXPECT_EQ(0, ret);

    munmap(addr2, ANON_SIZE);
}

} // namespace XpmUnitTest
} // namespace Security
} // namespace OHOS